<?php
namespace sys;

class Extend {
	
	//错误信息
	public $error = '';
	
	//获取模块或者插件的配置数据
	public function getInfo($Name,$Type = 1){
		//type 1模块 2插件
		if($Type == 1){
			$Model = model('app\admin\model\Module');
			$Path = APP_PATH  .  $Name . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'config.php';
		}else{
			$Model = model('app\admin\model\Plug');
			$Path = PLUG_PATH .  $Name . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'config.php';
		}
		//如果配置不存在，则返回错误
		if (!file_exists($Path)) {
			$this->error='目录不可读或者不存在';
			return false;
		}
		$Conf = require_once($Path);
		//数据库中是否存在配置信息
		$Info = $Model->find($Conf['id']);
		if(!$Info){
			$Conf['status']=-1;
		}else{
			$Conf = array_merge($Conf,$Info->toArray());
		}
		return $Conf;
	}
	//依赖检测
	public function _RelyCacke($List,$Arr,$Type){
		$Tit = ($Type==1)?'模块':'插件';
		//循环判断所有的依赖模块
		foreach($Arr as $V){
			//检测是否存在此模块
			if(!array_key_exists($V['use'],$List)){
				$this->error='依赖'.$Tit.' ['.$V['use'].'] 未安装';
				return false;
				break;
			}
			//检测是否存在此模块
			if(version_compare($V['version'],$List[$V['use']],'>')){
				$this->error='依赖'.$Tit.' ['.$V['use'] . '] 版本过低 现有：'.$List[$V['use']].' 最低版本需要:'.$V['version'];
				return false;
				break;
			}
			if(isset($V['end_version'])){
				//检测是否存在此模块
				if(version_compare($V['end_version'],$List[$V['use']],'<')){
					$this->error='依赖'.$Tit.' ['.$V['use'] . '] 版本过高 现有：'.$List[$V['use']].' 最高版本需要:'.$V['end_version'];
					return false;
					break;
				}
			}
		}
		return true;
	}
	//依赖写入
	public function _RelyWrite($List,$Model,$Use,$RelyOn=[]){
		foreach($List as $K => $V){
			$Rely = $Model->where(['use'=>$V['use']])->find();
			if(!in_array($Rely['rely_by'],[null,''])){
				$Rely['rely_by'] = explode(',',$Rely['rely_by']);
			}
			$Rely['rely_by'][] = $Use;
			$Rely['rely_by'] = implode(',',$Rely['rely_by']);
			$Model->update(['id' => $Rely['id'],'rely_by' => $Rely['rely_by']]);
			$RelyOn = $V['use'];
		}
		return $RelyOn;
	}
	//依赖移出
	public function _RelyDelete($List,$Model,$Use){
		foreach($List as $K => $V){
			$Rely = $Model->where(['use'=>$V['use']])->find();
			
			$Rely['rely_by'] = explode(',',$Rely['rely_by']);
			foreach ($Rely['rely_by'] as $K2=>$V2){
			  if ($V2 == $Use){
				  unset($Rely['rely_by'][$K2]);
			  }
			}
			$Rely['rely_by'] = implode(',',$Rely['rely_by']);
			$Model->update(['id' => $Rely['id'],'rely_by' => $Rely['rely_by']]);
		}
	}
	//安装前检测
	public function _InstBefore($Name,$Type = 1){
		$Info = $this->getInfo($Name,$Type);
		if($Info === false){
			return false;
		}
		if($Info['status'] != -1){
			$this->error = ($Type==1)?'模块 '.$Info['title'].'['.$Info['name'].'] 已经安装':'插件 '.$Info['title'].'['.$Info['name'].'] 已经安装';
			return false;
		}
		//检测依赖模块
		$ModuleList	= db('Module')->column('version','use');
		$Result		= $this->_RelyCacke($ModuleList,$Info['rely_module'],1);
		if($Result === false){
			return false;
		}
		$PlugList	= db('Plug')->column('version','use');
		$Result		= $this->_RelyCacke($PlugList,$Info['rely_plug'],2);
		if($Result === false){
			return false;
		}
		return $Info;
	}
	
	//安装
	function _Inst($Name,$Type = 1) {
		$Info = $this->_InstBefore($Name,$Type);
		if($Info === false){
			return false;
		}
		$Info['status'] = 1;
		if($Type == 1){
			$Validate	= validate ( 'app\admin\validate\Module' );
			$Model		= model('app\admin\model\Module');
			$Path		= APP_PATH  .  $Name . DIRECTORY_SEPARATOR;
			$StaticPath	= SYS_PATH.DIRECTORY_SEPARATOR.'static'.DIRECTORY_SEPARATOR.$Name;
		}else{
			$Validate	= validate ( 'app\admin\validate\Plug' );
			$Model		= model('app\admin\model\Plug');
			$Path		= PLUG_PATH .  $Name . DIRECTORY_SEPARATOR;
			$StaticPath	= SYS_PATH.DIRECTORY_SEPARATOR.'static'.DIRECTORY_SEPARATOR.'plug'.DIRECTORY_SEPARATOR.$Name;
		}
		// 验证数据 场景为add
		if (!$Validate->scene ( 'add' )->check ( $Info )) {
			// 验证失败 获取验证器的错误信息
			$Err = $Validate->getError ();
			$this->error = $Err ? $Err : '数据验证失败';
			return false;
		}
		
		//判断是否存在安装的sql文件
		if (file_exists($Path . 'data' . DIRECTORY_SEPARATOR . 'inst.sql')) {
			//获取sql文件的内容
			$Sql = file_get_contents($Path . 'data' . DIRECTORY_SEPARATOR . 'inst.sql');
			//对文件的表前缀替换
			$Sql = _SqlSplit($Sql, config('database.prefix'));
			$Result = _SqlRun($Sql);
			//检测是否存在此模块
			if(!$Result){
				$this->error = '安装Sql文件执行错误文件出错';
				return false;
			}
		}
		//判断是否存在函数扩展文件
		if (file_exists($Path . 'data' . DIRECTORY_SEPARATOR . 'func.php')) {
			$Result = copy($Path . 'data' . DIRECTORY_SEPARATOR . 'func.php',APP_PATH. 'common/func/'.str_replace('.','_',$Info['use']).'.php');
			if(!$Result){
				$this->error = '移动函数扩展文件出错';
				return false;
			}
		}
		//判断是否存在前置独立安装文件
		if (file_exists($Path . 'data' . DIRECTORY_SEPARATOR.'inst_before.php')) {
			//执行扩展文件
			@include ($Path . 'data' . DIRECTORY_SEPARATOR.'inst_before.php');
		}
		//移动静态资源文件夹
		if(is_dir($Path . 'data' . DIRECTORY_SEPARATOR.'static')){
			$Result = by_dir($StaticPath);
			if(!$Result){
				$this->error = '创建移动静态资源文件夹失败';
				return false;
			}
			$DirObj = new \sys\Dir();
			$DirObj->copyDir($Path . 'data' . DIRECTORY_SEPARATOR.'static',$StaticPath,true);
		}
		$RelyOn=[];
		$RelyModel = db('Module');
		$RelyOn = $this->_RelyWrite($Info['rely_module'],$RelyModel,$Info['use'],$RelyOn);
		$RelyModel = db('Plug');
		$RelyOn = $this->_RelyWrite($Info['rely_plug'],$RelyModel,$Info['use'],$RelyOn);
		$Info['rely_on'] = $RelyOn;
		if(array_key_exists('setting',$Info)){
			$Info['setting'] = serialize($Info['setting']);
		}else{
			$Info['setting'] = serialize([]);
		}
		//将安装好的扩展存入数据库
		$Result = $Model->allowField ( true )->isUpdate ( false )->save ( $Info );
		//判断是否存在后置扩展文件
		if (file_exists($Path . 'data' . DIRECTORY_SEPARATOR . 'inst.php')) {
			//执行扩展文件
			@include ($Path . 'data' . DIRECTORY_SEPARATOR . 'inst.php');
		}
		return true;
	}
	
	//卸载前检测
	function _UninBefore($Name,$Type = 1) {
		$Info = $this->getInfo($Name,$Type);
		if($Info === false){
			return false;
		}
		if($Info['status'] == -1){
			$this->error = ($Type==1)?'模块 '.$Info['title'].'['.$Info['name'].'] 尚未安装':'插件 '.$Info['title'].'['.$Info['name'].'] 尚未安装';
			return false;
		}
		if(!in_array($Info['rely_by'],[null,''])){
			return '此功能被 ['.$Info['rely_by'].'] 依赖 禁止卸载';
		}
		return $Info;
	}
	
	//卸载
	function _Unin($Name,$Type = 1) {
		$Info = $this->_UninBefore($Name,$Type);
		if($Info === false){
			return false;
		}
		if($Type == 1){
			$Model		= model('app\admin\model\Module');
			$Path		= APP_PATH  .  $Name . DIRECTORY_SEPARATOR;
			$StaticPath	= SYS_PATH.DIRECTORY_SEPARATOR.'static'.DIRECTORY_SEPARATOR.$Name;
		}else{
			$Model		= model('app\admin\model\Plug');
			$Path		= PLUG_PATH .  $Name . DIRECTORY_SEPARATOR;
			$StaticPath	= SYS_PATH.DIRECTORY_SEPARATOR.'static'.DIRECTORY_SEPARATOR.'plug'.DIRECTORY_SEPARATOR.$Name;
		}
		if($Type != 1){
			//删除ActionPlug中的记录
			db('ActionPlug')->where(['plug_id'=>$Info['id']])->delete();
		}
		//删除行为中的记录
		db('Action')->where(['identifying'=>$Info['id']])->delete();
		//删除节点中的记录
		db('AuthRule')->where(['identifying'=>$Info['id']])->delete();
		
		//判断是否存在卸载的sql文件
		if (file_exists($Path . 'data' . DIRECTORY_SEPARATOR . 'unin.sql')) {
			//获取sql文件的内容
			$Sql = file_get_contents($Path . 'data' . DIRECTORY_SEPARATOR . 'unin.sql');
			//对文件的表前缀替换
			$Sql = _SqlSplit($Sql, config('database.prefix'));
			$Result = _SqlRun($Sql);
			//检测是否存在此模块
			if(!$Result){
				$this->error = '卸载Sql文件执行错误文件出错';
				return false;
			}
		}
		//判断是否存在前置扩展文件
		if (file_exists($Path . 'data' . DIRECTORY_SEPARATOR . 'unin_before.php')) {
			//执行扩展文件
			@include ($Path . 'data' . DIRECTORY_SEPARATOR . 'unin_before.php');
		}
		//判断是否存在函数扩展文件
		if (file_exists(APP_PATH. 'common/func/'.str_replace('.','_',$Info['use']).'.php')) {
			$Result = unlink(APP_PATH. 'common/func/'.str_replace('.','_',$Info['use']).'.php');
			if(!$Result){
				$this->error = '删除函数文件出错';
				return false;
			}
		}
		//删除静态资源文件夹
		if(is_dir($StaticPath)){
			$DirObj = new \sys\Dir();
			$DirObj->delDir($StaticPath);
		}
		$RelyModel = db('Module');
		$this->_RelyDelete($Info['rely_module'],$RelyModel,$Info['use']);
		$RelyModel = db('Plug');
		$this->_RelyDelete($Info['rely_plug'],$RelyModel,$Info['use']);
		
		$Result = $Model->destroy ($Info['id']);
		if(!$Result){
			$_Err = $_Model->getError();
			$this->error = $_Err ? $_Err : "操作失败";
			return false;
		}
		//判断是否存在后置扩展文件
		if (file_exists($Path . 'data' . DIRECTORY_SEPARATOR . 'unin.php')) {
			//执行扩展文件
			@include ($Path . 'data' . DIRECTORY_SEPARATOR . 'unin.php');
		}
		//返回
		return true;
	}
}
